package ocp.context.xml;

import java.util.Enumeration;
import ocp.context.*;
import org.xml.sax.*;
import java.util.Vector;
import ocp.context.adapter.*;
import ocp.context.source.ContextAdapterDefinition;

/**
 * This class implements a xml reader for the definition of a contextual 
 * adapter. This definition comes with an schema (verified outside) and its
 * composed of several context class definitions and an adapter definition.
 *
 * @author Ignacio Nieto Carvajal.
 */
public class XMLContextAdapterReader extends XMLContextReader
{
        protected int state = INITIAL_STATE;
        protected ContextAdapterDefinition caDefinition;
        protected ContextClass cc;
        protected ContextPropertyDefinition cpd;
        
        protected static final int INITIAL_STATE = 0;
        protected static final int CONTEXT_STATE = 1;
        protected static final int ATT_STATE = 2;
        protected static final int PROP_STATE = 3;
        protected static final int DOM_STATE = 4;
        protected static final int RAN_STATE = 5;
        protected static final int ADAPT_STATE = 6;
        protected static final int MET_STATE = 7;
	protected static final int END_STATE = 8;
        
	public XMLContextAdapterReader(String xmlFile) { 
            super(xmlFile); 
            caDefinition = new ContextAdapterDefinition();
        }
	
	public XMLContextAdapterReader(String xmlFile, String parserName) {
		super(xmlFile, parserName);
                caDefinition = new ContextAdapterDefinition();
	}

	public void startElement(String uri, String localName, String qName, Attributes atts)
        throws SAXException
	{
            if (state == INITIAL_STATE) {
                if (localName.equals("ContextAdapter")) {
                    caDefinition.setName(atts.getValue("name"));
                    caDefinition.setOwner(atts.getValue("owner"));
                    caDefinition.setVersion(atts.getValue("version"));
                    state = CONTEXT_STATE;
                }
            }
            else if (state == CONTEXT_STATE) {
                if (localName.equals("ContextClass")) {
                    // TODO: Cambiar para que encuentre la clase.
                    cc = new ContextClass(atts.getValue("class"),null);
                    
                    ////
                    if (atts.getValue("historical")!=null){
                        if (atts.getValue("historical").equalsIgnoreCase("true")){
                            cc.setHistorical(true);
                        }
                    }
                    
                    state = ATT_STATE;
                }
            }
            else if (state == ATT_STATE) {
                if (localName.equals("AttributeDefinition")) {
                    AttributeDefinition ad = new AttributeDefinition(atts.getValue("name"), atts.getValue("type"));
                    cc.addAttributeDefinition(ad);
                }
                else if (localName.equals("ContextPropertyDefinition")) {
                    cpd = new ContextPropertyDefinition(atts.getValue("name"));
                    if (atts.getValue("transitive").equals("true")) cpd.setTransitive(true);
                    if (atts.getValue("functional").equals("true")) cpd.setFunctional(true);
                    if (atts.getValue("symmetric").equals("true")) cpd.setSymmetric(true);
                    if (!atts.getValue("inverse").equals("")) { 
                        ContextPropertyDefinition inverse = new ContextPropertyDefinition(atts.getValue("inverse"), cpd);
                        for (Enumeration e1 = cpd.getDomains(); e1.hasMoreElements();) 
                            inverse.addRange((ContextPropertySpace) e1.nextElement() );
                        for (Enumeration e2 = cpd.getRanges(); e2.hasMoreElements();) 
                            inverse.addDomain((ContextPropertySpace) e2.nextElement() );
                        cpd.setInverse(inverse);
                    }
                    state = DOM_STATE;
                }
            }
            else if (state == PROP_STATE) {
                if (localName.equals("ContextPropertyDefinition")) {
                    cpd = new ContextPropertyDefinition(atts.getValue("name"));
                    if (atts.getValue("transitive").equals("true")) cpd.setTransitive(true);
                    if (atts.getValue("functional").equals("true")) cpd.setFunctional(true);
                    if (atts.getValue("symmetric").equals("true")) cpd.setSymmetric(true);
                    if (!atts.getValue("inverse").equals("")) { 
                        ContextPropertyDefinition inverse = new ContextPropertyDefinition(atts.getValue("inverse"), cpd);
                        for (Enumeration e1 = cpd.getDomains(); e1.hasMoreElements();) 
                            inverse.addRange((ContextPropertySpace) e1.nextElement() );
                        for (Enumeration e2 = cpd.getRanges(); e2.hasMoreElements();) 
                            inverse.addDomain((ContextPropertySpace) e2.nextElement() );
                        cpd.setInverse(inverse);
                    }
                    state = DOM_STATE;                    
                }
            }
            else if (state == DOM_STATE) {
                if (localName.equals("Domain")) {
                    int type = atts.getValue("restriction").equals("SOME_VALUES_FROM")?ContextPropertySpace.SOME_VALUES_FROM:ContextPropertySpace.ALL_VALUES_FROM;
                    int min = ContextPropertySpace.UNKNOWN_CARDINALITY;
                    int max = ContextPropertySpace.UNKNOWN_CARDINALITY;
                    if (!atts.getValue("min").equals(""))
                        min = new Integer(atts.getValue("min")).intValue();
                    if (!atts.getValue("max").equals(""))
                        max = new Integer(atts.getValue("max")).intValue();
                    
                    ContextPropertySpace cs = new ContextPropertySpace(cc.getName(), min, max, type);
                    cpd.addDomain(cs);
                }
                else if (localName.equals("Range")) {
                    state = RAN_STATE;
                    int type = atts.getValue("restriction").equals("SOME_VALUES_FROM")?ContextPropertySpace.SOME_VALUES_FROM:ContextPropertySpace.ALL_VALUES_FROM;
                    int min = ContextPropertySpace.UNKNOWN_CARDINALITY;
                    int max = ContextPropertySpace.UNKNOWN_CARDINALITY;
                    if (!atts.getValue("min").equals(""))
                        min = new Integer(atts.getValue("min")).intValue();
                    if (!atts.getValue("max").equals(""))
                        max = new Integer(atts.getValue("max")).intValue();
                    
                    ContextPropertySpace cs = new ContextPropertySpace(cc.getName(), min, max, type);
                    cpd.addRange(cs);
                }
            }
            else if (state == RAN_STATE) {
                    int type = atts.getValue("restriction").equals("SOME_VALUES_FROM")?ContextPropertySpace.SOME_VALUES_FROM:ContextPropertySpace.ALL_VALUES_FROM;
                    int min = ContextPropertySpace.UNKNOWN_CARDINALITY;
                    int max = ContextPropertySpace.UNKNOWN_CARDINALITY;
                    if (!atts.getValue("min").equals(""))
                        min = new Integer(atts.getValue("min")).intValue();
                    if (!atts.getValue("max").equals(""))
                        max = new Integer(atts.getValue("max")).intValue();
                    
                    ContextPropertySpace cs = new ContextPropertySpace(cc.getName(), min, max, type);
                    cpd.addRange(cs);
            }
            else if (state == ADAPT_STATE) {
                if (localName.equals("Adapter")) {
                    caDefinition.setAdapterName(atts.getValue("name"));
                    caDefinition.setAdapterClass(atts.getValue("class"));
                    state = MET_STATE;
                }
            }
            else if (state == MET_STATE) {
                if (localName.equals("Method")) {
                    caDefinition.addAdapterMethod(atts.getValue("name"), 
                            atts.getValue("attribute"), atts.getValue("type"));
                }
            }
	}

	public void endElement(String uri, String localName, String qName) 
        throws SAXException
	{
            // si termina el contextclass añadirle los elementos (dominios, rangos, propiedades, attribute...) e
            // incluirlo en el contextadapterdefinition. y ademas poner que está en estado contextclass tb (pq
            // puede haber m�s de una).
            if (localName.equals("ContextPropertyDefinition")) {
                state = PROP_STATE;
                cc.addPropertyDefinition(cpd);
            }
            else if (localName.equals("ContextClass")) {
                state = ADAPT_STATE;
                caDefinition.setContextClass(cc);
            }
            else if (localName.equals("Adapter")) {
                state = END_STATE;
            }
            else if (localName.equals("ContextAdapter")) {
                if (state == END_STATE) System.out.println("Context Adapter parsed successfully");
                else System.err.println("Error parsing Context Adapter "+caDefinition.getName());
            }
	}

	public Object readContext() throws IncompatibleContextualObjectException {
            if (state == END_STATE) return caDefinition;
            else throw new IncompatibleContextualObjectException("Error parsing Context Adapter "+caDefinition.getName());
	}
}
